#include "health_impl.h"
#include "cfg.h"
#include "common.h"
#include "formula.h"
#include "impl.h"
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MATCH_APPEND(cond)                                                     \
  for (size_t j = 0; j < ct; j++) {                                            \
    size_t t_offset = *(temp + j);                                             \
    if ((cond)) {                                                              \
      *(filter_list + (f++)) = t_offset;                                       \
    }                                                                          \
  }

#define GEN_FILTER(x_list, len, cond)                                          \
  ct = 0;                                                                      \
  for (size_t i = 0; i < len; i++) {                                           \
    size_t x_offset = *(x_list + i);                                           \
    if (!ct) {                                                                 \
      *(temp + (ct++)) = x_offset;                                             \
      continue;                                                                \
    }                                                                          \
    int64_t ts_diff =                                                          \
        (int64_t)((list + x_offset)->ts - (list + *(temp + ct - 1))->ts);      \
    if (ABS(ts_diff) <= 6 * 60 * 1000) {                                       \
      *(temp + (ct++)) = x_offset;                                             \
      continue;                                                                \
    }                                                                          \
    MATCH_APPEND(cond)                                                         \
    *temp = x_offset;                                                          \
    ct = 1;                                                                    \
  }                                                                            \
  MATCH_APPEND(cond)

typedef struct _ {
  uint16_t bar;
  uint16_t _r1;
  uint32_t _r2;
  // 系数
  double rate;
} strip_ab_t;

/*
封装smp对象和下标
用于排序
 */
typedef struct _wrap_smp {
  size_t index;
  const smp_hr_t *sp;
} wrap_smp_t;

static strip_ab_t _choose[] = {
    {65, 0, 0, 1.2},
    {85, 0, 0, 1.4},
    {0xffff, 0, 0, 1.6},
};

static size_t SIZE_SMP = sizeof(smp_hr_t);

static uint16_t _p2u16(const void *v) { return *((uint16_t *)v); }

static uint64_t _wrap2u64(const void *v) {
  wrap_smp_t *s = (wrap_smp_t *)v;
  return s->sp->ts;
}

NEW_CMP(_cmp_u16, uint16_t, _p2u16)
NEW_CMP(_cmp_ts, uint64_t, _wrap2u64)

/*
去除最小值和最大值，取心率平均值
 */
static double _avg_hr(const smp_hr_t *list, size_t len, double *full_avg) {
#ifndef _WIN32
  uint32_t hr[len];
#else
  uint32_t *hr = (uint32_t *)malloc(sizeof(uint32_t) * len);
#endif

  for (size_t i = 0; i < len; i++) {
    uint32_t cur = (list + i)->rate;
    hr[i] = cur;
  }

  *full_avg = avg_uint32_t(hr, len);

  qsort(hr, len, sizeof(uint16_t), _cmp_u16);
  double au = avg_uint32_t(hr + 1, len - 2);

#ifdef _WIN32
  free(hr);
#endif

  return au;
}

static void _filter_ab(smp_hr_t *list, size_t len, double strip_avg,
                       double rate, size_t *a_list, size_t *sa, size_t *b_list,
                       size_t *sb) {
  if (!a_list || !b_list || !sa || !sb) {
    return;
  }

  // 俩list的个数
  size_t a = 0, b = 0;
  for (size_t i = 0; i < len; i++) {
    smp_hr_t *cur = list + i;

    if (cur->rate > strip_avg * rate) {
      *(a_list + (a++)) = i;
    }
    if (cur->rate < strip_avg / rate) {
      *(b_list + (b++)) = i;
    }
  }

  *sa = a;
  *sb = b;
}

#ifdef __gnu_linux__
__attribute__((unused))
#endif
static void
_expr(const smp_hr_t *full_list, const size_t *x_list, size_t len,
      const char *txt) {
  if (!len) {
    printf("%s empty\n", txt);
    return;
  }

  printf("> %s\n", txt);
  for (size_t i = 0; i < len; i++) {
    const smp_hr_t *cur = full_list + *(x_list + i);
    printf("%d, %d, %d\n", cur->rate, cur->hrv, cur->motion);
  }
}

/*
构建哈希表
@param ht: hashtable
 */
static uint8_t *_sp2hash(size_t sl, const size_t *filter_list, size_t fl,
                         size_t *onum) {
  NOTUSED(sl);

  *onum = 0;
  if (!fl)
    return NULL;

  size_t num;

  /*
  如使用max构建哈希
  则可保证空间最小
  但需耗费cpu指令找出max

  考虑到实际情况
  直接使用sl作为哈希长度
  也不至于浪费太多空间
  且可节省cpu耗时
   */

#if 1
  size_t max = 0;

  for (size_t i = 0; i < fl; i++) {
    /*
    找出最大值
    申请空间
     */
    size_t each = *(filter_list + i);
    if (each > max) {
      max = each;
    }
  }

  num = max + 1;
#else
  num = sl;
#endif

  // 保证调用方判断正确
  *onum = num;

  uint8_t *ht = (uint8_t *)calloc(num, sizeof(uint8_t));
  if (!ht)
    return NULL;

  for (size_t i = 0; i < fl; i++) {
    // 下标置1,表示该位置需排除
    *(ht + *(filter_list + i)) = 1;
  }

  return ht;
}

static smp_hr_t *_ht_ex(const smp_hr_t *list, size_t sl, size_t fl, uint8_t *ht,
                        size_t nhash, double *rate_hr, size_t *nout) {
  size_t left = sl - fl;
  smp_hr_t *output = (smp_hr_t *)malloc(sizeof(smp_hr_t) * left);
  if (!output) {
    if (ht)
      free(ht);
    return NULL;
  }

  size_t offset = 0;
#ifndef _WIN32
  uint32_t hr_list[left];
#else
  uint32_t *hr_list = (uint32_t *)malloc(sizeof(uint32_t) * left);
#endif

  for (size_t i = 0; i < sl; i++) {
    if (ht && i <= nhash - 1 && ht[i]) {
      /*
      ht非空，说明fl>0
      命中哈希，说明需要排除
      O(1)查询
       */
      continue;
    }

    const smp_hr_t *cur = list + i;

    memcpy(output + offset, cur, SIZE_SMP);
    *(hr_list + offset) = cur->rate;

    offset++;
  }
  *rate_hr = avg_uint32_t(hr_list, left);
  if (ht)
    free(ht);

#ifdef _WIN32
  free(hr_list);
#endif

  *nout = left;
  return output;
}

/*
从全量心率中排除
@param sl: size of smp list
@param fl: size of filter list
 */
static smp_hr_t *_exclude(const smp_hr_t *list, size_t sl,
                          const size_t *filter_list, size_t fl, double *rate_hr,
                          size_t *nout) {
  *rate_hr = 0;
  *nout = 0;

  if (!sl)
    return NULL;
  if (fl && sl <= fl) {
    // = 导致申请内存ub
    return NULL;
  }

  size_t nhash;
  uint8_t *ht = _sp2hash(sl, filter_list, fl, &nhash);
  if (nhash && !ht) {
    // 哈希表创建失败
    return NULL;
  }

  return _ht_ex(list, sl, fl, ht, nhash, rate_hr, nout);
}

/*
携带索引排序
 */
static void _sort_ab(size_t *x_list, size_t xl, const smp_hr_t *ref_list,
                     size_t rl) {
  NOTUSED(rl);

  if (!xl) {
    return;
  }

#ifndef _WIN32
  wrap_smp_t wlist[xl];
#else
  wrap_smp_t *wlist = (wrap_smp_t *)malloc(sizeof(wrap_smp_t) * xl);
#endif

  size_t index;

  for (size_t i = 0; i < xl; i++) {
    wrap_smp_t *set = &wlist[i];

    index = *(x_list + i);

    set->index = index;
    set->sp = ref_list + index;
  }

  // 排序之后，再将下标写回
  qsort(wlist, xl, sizeof(wrap_smp_t), _cmp_ts);

  for (size_t i = 0; i < xl; i++) {
    *(x_list + i) = (wlist + i)->index;
  }

#ifdef _WIN32
  free(wlist);
#endif
}

static smp_hr_t *_impl(smp_hr_t *list, size_t len, double full_avg,
                       double strip_avg, double *rate_out, size_t *nout) {
  NOTUSED(full_avg);

#ifndef _WIN32
  size_t a_list[len];
  size_t b_list[len];
#else
  size_t *a_list = (size_t *)malloc(sizeof(size_t) * len);
  size_t *b_list = (size_t *)malloc(sizeof(size_t) * len);
#endif

  // size of a list
  size_t sa = 0, sb = 0;

  for (uint8_t i = 0; i < ARRAY_SIZE(_choose); i++) {
    strip_ab_t *cur = &_choose[i];

    if (strip_avg <= cur->bar) {
      _filter_ab(list, len, strip_avg, cur->rate, a_list, &sa, b_list, &sb);
      break;
    }
  }

  _sort_ab(a_list, sa, list, len);
  _sort_ab(b_list, sb, list, len);

  // _expr(list, a_list, sa, "alist");

#ifndef _WIN32
  size_t temp[len];
  size_t filter_list[len];
#else
  size_t *temp = (size_t *)malloc(sizeof(size_t) * len);
  size_t *filter_list = (size_t *)malloc(sizeof(size_t) * len);
#endif

  size_t ct;
  size_t f = 0;

  GEN_FILTER(a_list, sa,
             ((list + t_offset)->rate >= strip_avg * 2 ||
              (list + t_offset)->motion <= 100))
  GEN_FILTER(b_list, sb, ((list + t_offset)->motion >= 50))

#ifdef _WIN32
  free(a_list);
  free(b_list);
#endif

  smp_hr_t *result = _exclude(list, len, filter_list, f, rate_out, nout);

#ifdef _WIN32
  free(temp);
  free(filter_list);
#endif

  return result;
}

smp_hr_t *analyze_hr(smp_hr_t *list, size_t len, double *avg_rate,
                     size_t *nout) {
  *avg_rate = 0;
  *nout = 0;
  if (!list)
    return NULL;

  if (len <= 3)
    return _ht_ex(list, len, 0, NULL, 0, avg_rate, nout);

  double full_avg;
  double strip_avg = _avg_hr(list, len, &full_avg);

  return _impl(list, len, full_avg, strip_avg, avg_rate, nout);
}